﻿namespace wToggle.Platform
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net.Http;
    using System.Threading.Tasks;

    using wToggle.Platform.Data;
    using wToggle.Platform.Interfaces;

    using XPlatUtils;

    public class ProjectProvider : IProjectProvider
    {
        public async Task<ICollection<Project>> GetProjects(ulong workspaceId)
        {
            if (workspaceId <= 0)
            {
                throw new ArgumentOutOfRangeException("workspaceId", "Invalid workspace ID specified");
            }

            var cached = CacheLayer.LoadEntities<Project>();

            if (cached != null)
            {
                var projects = cached.Where(p => p.WorkspaceId == workspaceId).ToList();
                if (projects.Any())
                {
                    return projects;
                }
            }

            var auth = ServiceContainer.Resolve<ITogglAuth>();

            using (var client = auth.GetPreauthenticatedClient())
            {
                var projects = await client.GetJsonAsync<List<Project>>("workspaces/" + workspaceId + "/projects");

                if (projects == null)
                {
                    return new List<Project>();
                }

                var ordered = projects.OrderBy(p => p.Name).ToList();

                await CacheLayer.StoreEntities(ordered);
                return ordered;
            }
        }

        public async Task<Project> GetProject(ulong projectId)
        {
            if (projectId <= 0)
            {
                throw new ArgumentOutOfRangeException("projectId", "Invalid project ID specified");
            }

            var cached = CacheLayer.LoadEntity<Project>(projectId);

            if (cached != null)
            {
                return cached;
            }

            var auth = ServiceContainer.Resolve<ITogglAuth>();

            try
            {
                using (var client = auth.GetPreauthenticatedClient())
                {
                    var project = await client.GetJsonAsync<ResponseData<Project>>("projects/" + projectId);

                    if (project == null)
                    {
                        return null;
                    }

                    await CacheLayer.StoreEntity(project.Data);

                    return project.Data;
                }
            }
            catch (InvalidOperationException e)
            {
                return null;
            }
        }

        public async Task<Project> Create(Project project, bool withLock = true)
        {
            if (project == null)
            {
                throw new ArgumentNullException("project");
            }

            if (Toggly.HasInternetAccess())
            {
                var auth = ServiceContainer.Resolve<ITogglAuth>();

                using (var client = auth.GetPreauthenticatedClient())
                {
                    var result =
                        await
                        client.PostJsonAsync<ResponseData<Project>, ProjectRequest>(
                            "projects",
                            new ProjectRequest { Data = project });

                    if (result == null)
                    {
                        return null;
                    }

                    project.Id = result.Data.Id;
                    await CacheLayer.StoreEntity(result.Data, withLock);

                    return result.Data;
                }
            }
            else
            {
                await CacheLayer.StoreEntryWrite(project, WriteType.Create, withLock);
                return project;
            }
        }

        public async Task<Project> Update(Project project, bool withLock = true)
        {
            if (project == null)
            {
                throw new ArgumentNullException("project");
            }

            if (project.Id <= 0)
            {
                throw new ArgumentException("Project ID out of range");
            }

            if (Toggly.HasInternetAccess())
            {
                var auth = ServiceContainer.Resolve<ITogglAuth>();

                using (var c = auth.GetPreauthenticatedClient())
                {
                    var result =
                        await
                        c.PutJsonAsync<ResponseData<Project>, ProjectRequest>(
                            "projects/" + project.Id,
                            new ProjectRequest { Data = project });

                    if (result == null)
                    {
                        return null;
                    }

                    await CacheLayer.StoreEntity(result.Data, withLock);

                    return result.Data;
                }
            }
            else
            {
                await CacheLayer.StoreEntryWrite(project, WriteType.Update, withLock);
                return project;
            }
        }

        public async Task<bool> Delete(ulong id, bool withLock = true)
        {
            if (id <= 0)
            {
                throw new ArgumentOutOfRangeException("id", "Project ID out of range");
            }

            if (Toggly.HasInternetAccess())
            {
                var auth = ServiceContainer.Resolve<ITogglAuth>();

                using (var client = auth.GetPreauthenticatedClient())
                {
                    var result = await client.DeleteAsync("projects/" + id);

                    try
                    {
                        result.EnsureSuccessStatusCode();

                        await CacheLayer.RemoveEntity<Project>(id, withLock);

                        return true;
                    }
                    catch (HttpRequestException e)
                    {
                        return false;
                    }
                }
            }
            else
            {
                var existing = CacheLayer.LoadEntity<Project>(id);

                if (existing == null)
                {
                    return true;
                }

                await CacheLayer.StoreEntryWrite(existing, WriteType.Delete, withLock);
                return true;
            }
        }
    }
}